<?php

/* ========================================================================= */
/* This file is part of the HypnoPHP Framework Library                       */
/* Copyright (c) 2009-2010, Radu Buzea                                       */
/* ========================================================================= */

/**
* @author Radu Buzea
* @copyright Copyright (c) 2009-2010, Radu Buzea (www.hypnosis.ro)
* @license GNU Lesser General Public License (www.gnu.org/copyleft/lesser.html)
* @package HypnoPHP
* @category Core
* @version 1.0.3
*/
defined('HYPNOPHP_APPLICATION') or
die("Cannot use <i>" . basename(__FILE__) . "</i> outside HypnoPHP Application");

/**
* Set Route example
* use this code in your [application folder]/config/routes.php file
* <code>
* HRouter::set(array(
* 	'rule'             => '/',                   // string - Regular Expression or URI
* 	'controller'       => 'Controller_Welcome',  // string - the Controller name
* 	'action'           => 'index',               // string - the Action name
* 	'regexp'           => false,                 // bool   - the rule is a Regular Expression
* 	'case_insensitive' => false,                 // bool   - the rule is URI and is case insensitive
* 	'content-type'     => 'text/html'            // string - the output Content-Type
* 	'params'           => null,                  // array  - associates values to keys parameters
* ));
* </code>
*/

// ============================================================================
// Final class: HRouter
// ============================================================================

final class HRouter extends HBaseObject
{

	/**
	* Initialize the router (static method)
	*
	*	@return void
	* @access public
	* @static
	*/
	public static function initRouter()
	{
		$router = self::getInstance();
		$router->routes = array();
		// Use $_HAPPLICATION_GLOBAL_ROUTES in the H_Globals.php to set system routes
		if (isset($_HAPPLICATION_GLOBAL_ROUTES) && is_array($_HAPPLICATION_GLOBAL_ROUTES)) {
			foreach ($_HAPPLICATION_GLOBAL_ROUTES as $route) { self::set($route); }
		}
	}

	/**
	* put your comment there...
	*
	* @param mixed $route
	* @return void
	* @access public
	* @static
	*/
	public static function set(array $route)
	{
		// Add a group of routes
		if (isset($route[0]['rule'])) {
			foreach ($route as $route_set) { self::set($route_set); }
			return;
		}
		// Add a new route
		if (isset($route['rule']) && trim($route['rule']) != '' &&
			isset($route['controller']) && trim($route['controller']) != '') {
			$route_key = md5($route['rule']);
			if (!isset($route['action']) || trim($route['action']) == '') {
				$route['action'] = 'index';
			}
			$rt_object = self::getInstance();
			$routes = $rt_object->routes;
			if (!is_array($routes)) { $routes = array(); }
			$routes[$route_key] = $route;
			$rt_object->routes = $routes;
		}
	}

	/**
	* put your comment there...
	*
	* @param mixed $route
	* @return void
	* @access public
	* @static
	*/
	public static function add(array $route)
	{
		self::set($route);
	}

	/**
	* put your comment there...
	*
	* @param string $uri
	* @return mixed Array or NULL
	* @access public
	* @static
	*/
	public static function specialRoute($uri = null)
	{
		if (is_string($uri)) {
			$p = strrpos($uri, "/");
			$original_uri = $uri;
			if ($p !== false) { $uri = substr($uri, $p + 1); }
			$uri = trim(strtolower($uri));
			switch ($uri) {
				case 'favicon.png':
					// This WILL BE SKIPPED if you put a physical file in the site root
					HRequest::params()->t = 0;
				case 'hypnophp_internal_ui_icon':
					// Image used in the html code generated in the HPrettyUI class
					$route['controller'] = 'HInternalUI';
					$route['action'] = 'hypnophpUIIconAction';
					$route['special_route'] = true;
					$route['params'] = array();
					return $route;
				case 'hypnophp_manager':
				case 'hypnophp_info':
					// Path used to call the HypnoPHP Manager methods from the HManager class
					// This WILL NOT BE SKIPPED if define route with a similar name
					$route['controller'] = 'HManager';
					$action_parts = preg_split('/_/', $uri);
					if (count($action_parts) == 2) {
						$route['action'] = $action_parts[0] . ucwords($action_parts[1]) . 'Action';
						if ($original_uri != 'hypnophp_manager') {
							$original_uri = trim(substr($original_uri, 0, -16), '/ ');
							$route['params'] = array('original_uri' => $original_uri);
						}
						return $route;
					}
			}
		}
		return null;
	}

	/**
	* put your comment there...
	*
	* @param mixed $uri String or Array
	* @return string
	* @access public
	* @static
	*/
	public static function findRoute($uri = null)
	{
		if ($uri == null) {
			// Get the internal requested URI string
			$uri = HRequest::internalRequestURI();
		} else if (is_array($uri)) {
			// Build the URI string from the $uri array paramaeter
			$uri_parts = $uri;
			$uri = '';
			foreach ($uri_parts as $part) { $uri .= '/' . $part; }
		} else if (!is_string($uri)) {
			// Invalid type for the $uri paramaeter
			return null;
		}
		$uri = trim($uri, '/');
		$qs_pos = strpos($uri, '?');
		if ($qs_pos !== false) {
			if (HApplication::rewriteMode()) {
				$uri = substr($uri, 0, $qs_pos);
			} else {
				$page = HRequest::params('page');
				if (is_string($page)) { $uri = trim($page, '/'); }
				else {
					$qs_next_pos = strpos($uri, '&', $qs_pos);
					if ($qs_next_pos !== false) {
						$uri = trim(substr($uri, $qs_pos + 1, $qs_next_pos - ($qs_pos + 1)), '/');
					} else {
						$uri = trim(substr($uri, $qs_pos + 1), '/');
					}
				}
			}
		}

		// Search and remove the site_root from the URI string
		$site_root = HApplication::getConfig('site_root');
		if (!empty($site_root)) {
			$site_root = trim($site_root, '/');
			if ($site_root != '') {
				$p = strpos($uri, $site_root);
				if ($p !== false && $p == 0) {
					$uri = trim(substr($uri, strlen($site_root)), '/');
				}
			}
		}

		// Fixing the problem with the first script file originally called
		// Usually is the application index.php file
		$included_files = get_included_files();
		$first_included = $included_files[0];
		$p = strrpos($first_included, DS);
		if ($p !== false) {
			$first_included = substr($first_included, $p + 1);
			if ($first_included == $uri) { $uri = ""; }
		}

		if ($uri != "") {
			// Search for special routes
			$route = self::specialRoute($uri);
			if (is_array($route) && isset($route['controller'])) { return $route; }
			else { unset($route); }
		}

		// Search for the route associated to the URI string
		$router = self::getInstance();
		$routes = $router->routes;

		if (is_array($routes) || count($routes) > 0) {
			$uri_search = '/' . $uri;
			$route_key = md5($uri_search);
			if (isset($routes[$route_key])) { return $routes[$route_key]; }
			foreach ($routes as $route) {
				if (isset($route['regexp']) && $route['regexp']) {
					// Check the route rule using regular expression
					if (preg_match($route['rule'], $uri_search)) { return $route; }
				} else {
					// Check the route rule using string comparison (case-sensitive or case-insensitive)
					$case_insensitive = (isset($route['case-insensitive']) && $route['case-insensitive']);
					if ($case_insensitive) { $comparison = strcasecmp($route['rule'], $uri_search); }
					else { $comparison = strcmp($route['rule'], $uri_search); }
					if ($comparison == 0) { return $route; }
				}
			}
		}

		// No route found - build a route for the requested URI
		// First, split the requested URI in parts (convert it into array)
		if (strstr($uri, '/')) { $uri_parts = explode('/', $uri); }
		else { $uri_parts = array($uri); }
		if (!is_array($uri_parts) || count($uri_parts) == 0) { return null; }
		if (!isset($uri_parts[0])) { return null; } // what the heck...
		$route = array();
		// ...use the first URI part as the controller name
		$route['controller'] = 'Controller_' . $uri_parts[0];
		// ...use the second URI part as the controller's action name
		$route['action'] = isset($uri_parts[1]) ? $uri_parts[1] : 'index';
		$route['params'] = array();
		// ...if there are more than 2 URI parts, use them as controller's action params
		$up_count = count($uri_parts);
		if ($up_count > 2) {
			$i = 2;
			while ($i < $up_count) {
				if (isset($uri_parts[$i + 1])) {
					$route['params'][urldecode($uri_parts[$i])] = urldecode($uri_parts[$i + 1]);
					$i = $i + 2;
				} else {
					$route['params'][urldecode($uri_parts[$i])] = null;
					break;
				}
			}
		}
		return $route;
	}

	/**
	* put your comment there...
	*
	* @param mixed $uri
	* @return mixed  Array or NULL
	* @access public
	* @static
	*/
	public static function getInstructions($uri)
	{
		// This method serch for a route by given URI string then
		// check the route structure: controller, action, and params.
		// This method is not used in this version of the core system.
		$route = self::findRoute($uri);
		if (is_array($route)) {
			if (!isset($route['action'])) {
				$route['action'] = 'index';
			}
			if (isset($route['controller']) && trim($route['controller']) != '') {
				$result = array(
					'controller' => $route['controller'],
					'action' => $route['action'],
				);
				if (!isset($route['params']) || !is_array($route['params'])) {
					if (isset($route['params'])) { $result['params'] = array($route['params']); }
					else { $result['params'] = array(); }
				}
				return $result;
			}
		}
		return false;
	}

	/**
	* put your comment there...
	*
	* @return object singleton
	* @access public
	* @static
	*/
	public static function getInstance()
	{
		return self::singleton(__CLASS__);
	}

}
